//
//  VideoPlayerPluginType.swift
//  VideoPlayer
//
//  Created by JY_NEW on 2021/10/8.
//

import Foundation
import UIKit
import IJKMediaFrameworkWithSSL
import Reachability
import UICreate
import PropertyWrappers

open class VideoPlayerManager: VideoPlayerManagerType {
    
    public let layerManager: VideoPlayerLayerManagerType = VideoPlayerLayerManager()
    
    public let observer: VideoPlayerManagerObserverType = VideoPlayerManagerObserver()
    
    private(set) public var currentPlayerAdapter: CorePlayerAdapterType? {
        didSet {
            if let old = oldValue {
                self.releasePlayerAdapter(old)
            }
            if let player = self.currentPlayerAdapter {
                player.delegate = self
                player.rate = self.rate
                self.view.insertSubview(player.view, at: 0)
                self.layoutVideoGravity()
            }
        }
    }
    private func releasePlayerAdapter(_ player: CorePlayerAdapterType) {
        player.delegate = nil
        player.stop()
        player.view.removeFromSuperview()
    }
    
    private(set) public var status: VideoPlayerStatus = .stoped(.unload) {
        didSet {
            if oldValue != self.status {
                self.observer.status.sendActions(for: self.status)
            }
        }
    }
    
    private(set) public var networkStatus: VideoPlayerNetworkStatus = .wifi {
        didSet {
            if oldValue != self.networkStatus {
                self.observer.networkStatus.sendActions(for: self.networkStatus)
            }
        }
    }
    
    public var isFullScreen: Bool {
        get {
            return UIApplication.shared.statusBarOrientation.isLandscape
        }
        set {
            if newValue != self.isFullScreen {
                let initValue = NSNumber(integerLiteral: UIInterfaceOrientation.unknown.rawValue)
                let v = NSNumber(integerLiteral: newValue ?  UIInterfaceOrientation.landscapeRight.rawValue :
                    UIInterfaceOrientation.portrait.rawValue )
                UIDevice.current.setValue(initValue, forKey: "orientation")
                UIDevice.current.setValue(v, forKey: "orientation")
            }
        }
    }
    /// 是否允许自动横竖屏，由ViewController来控制
    public var shouldAutorotate: Bool = true {
        didSet {
            self.observer.shouldAutorotate.sendActions(for: self.shouldAutorotate)
        }
    }
    
    /// 播放器视图
    public lazy var view: UIView = {
        let view = ContainerView()
        view.backgroundColor = .black
        view.whenRelayouted = { [weak self] in
            self?.layoutVideoGravity()
        }
        return view
    }()
    
    public var rate: Float = 1 {
        didSet {
            self.currentPlayerAdapter?.rate = self.rate
            self.observer.rate.sendActions(for: self.rate)
        }
    }
    
    public var volume: Float {
        get { return AVAudioSession.sharedInstance().outputVolume }
        set {
            self._systemVolumeSlider.setValue(newValue, animated: false)
            self._systemVolumeSlider.sendActions(for: .touchUpInside)
            self.observer.volume.sendActions(for: self.volume)
        }
    }
    /// 用于控制系统声音
    private lazy var _systemVolumeSlider: UISlider = {
        let systemVolumView = MPVolumeView()
        self.view.addSubview(systemVolumView)
        systemVolumView.snp.makeConstraints { make in
            make.top.left.equalTo(-100)
            make.width.height.equalTo(40)
        }
        systemVolumView.showsVolumeSlider = true
        if let slider = systemVolumView.subviews.first(where: { type(of: $0).description() == "MPVolumeSlider" }) as? UISlider {
            return slider
        }else{
            return UISlider()
        }
    }()
    
    /// 亮度
    public var brightness: Float {
        get { return Float(UIScreen.main.brightness) }
        set { UIScreen.main.brightness = CGFloat(newValue) }
    }
    
    public var videoGravity: VideoPlayerGravity = .default {
        didSet {
            self.layoutVideoGravity()
            self.observer.videoGravity.sendActions(for: self.videoGravity)
        }
    }
    
    private var waitToPlayTime: TimeInterval? = nil
    public var currentTime: TimeInterval {
        get {
            return self.currentPlayerAdapter?.currentTime ?? 0
        }
        set {
            if self.status.isPlaying {
                self.seek(to: newValue, completion: nil)
            }else{
                self.waitToPlayTime = newValue
            }
        }
    }
    
    public var duration: TimeInterval {
        return self.currentPlayerAdapter?.duration ?? 0
    }
    
    public var playableDuration: TimeInterval { return self.currentPlayerAdapter?.playableDuration ?? 0 }
    
    public let reachability = Reachability.forInternetConnection()
    
    /// 用于存放暂停标记
    @PWAtomic
    private var pauseFlags: [Int] = []
    
    public let ijkOptions = IJKFFOptions.byDefault()!
    
    /// 播放链接
    public var url: URL? {
        didSet {
            self.status = .stoped(.exited)
            if let url = self.url {
                self.currentPlayerAdapter = IJKPlayerAdapter(contentURL: url, with: self.ijkOptions)
            }else{
                self.currentPlayerAdapter = nil
            }
            self.observer.url.sendActions(for: self.url)
        }
    }
    
    private var enterBackgroundPauseFlag = makePauseFlag()
    private var isInBackground = false
    
    public init() {
        self.layerManager.playerManager = self
        NotificationCenter.default.addObserver(self, selector: #selector(self.onEnterBackground), name:UIApplication.willResignActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.onEnterForeground), name:UIApplication.didBecomeActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.onRotation), name: UIApplication.didChangeStatusBarOrientationNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.onReachabilityChanged), name:.reachabilityChanged, object: nil)
        self.onReachabilityChanged()
        self.reachability?.startNotifier()
    }
    deinit {
        if let currentPlayerAdapter = currentPlayerAdapter {
            self.currentPlayerAdapter = nil
            self.releasePlayerAdapter(currentPlayerAdapter)
        }
        NotificationCenter.default.removeObserver(self)
        self.reachability?.stopNotifier()
    }
    @objc private func onEnterBackground() {
        //每次都会调两次，而且从上往下拉左边区域时还会反复调造成在后台还在播放，所以加个延时判断是否0.1秒后还在后台
        self.isInBackground = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            if self.isInBackground && !self.isPaused(for: self.enterBackgroundPauseFlag){
                self.pause(for: self.enterBackgroundPauseFlag)
            }
        }
    }
    @objc private func onEnterForeground() {
        self.isInBackground = false
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            if !self.isInBackground && self.isPaused(for: self.enterBackgroundPauseFlag){
                self.play(for: self.enterBackgroundPauseFlag)
            }
        }
    }
    @objc private func onReachabilityChanged() {
        let status = self.reachability?.currentReachabilityStatus() ?? .ReachableViaWiFi
            switch status {
            case .NotReachable:
                self.networkStatus = .unreachable
            case .ReachableViaWiFi:
                self.networkStatus = .wifi
            case .ReachableViaWWAN:
                self.networkStatus = .wwan
            @unknown default:
                self.networkStatus = .wifi
            }
    }
    
    @objc private func onRotation() {
        self.observer.fullScreen.sendActions(for: self.isFullScreen)
    }
    
    /// 跳转到指定时长
    public func seek(to time: TimeInterval, completion: ((Bool) -> Void)?) {
        self.currentPlayerAdapter?.seek(to: time, completion: completion)
    }
    
    public func play() {
        if let waitToPlayTime = self.waitToPlayTime {
            self.waitToPlayTime = nil
            self.play(from: waitToPlayTime)
        }else{
            self.pauseFlags.removeAll()
            if self.status.isError {
                self.refresh()
            }
            self.currentPlayerAdapter?.play()
        }
    }
    public func play(from time: TimeInterval) {
        self.pauseFlags.removeAll()
        if self.status.isError {
            self.refresh()
        }
        self.seek(to: time, completion: nil)
    }
    
    public func pause() {
        self.pauseFlags.removeAll()
        self.currentPlayerAdapter?.pause()
    }
    public func refresh() {
        if let url = self.url {
            self.currentPlayerAdapter = IJKPlayerAdapter(contentURL: url, with: self.ijkOptions)
        }
    }
    
    private func stop() {
        self.pauseFlags.removeAll()
        self.currentPlayerAdapter?.stop()
    }
    
    public func play(for flag: Int) {
        if let idx = self.pauseFlags.firstIndex(of: flag) {
            self.pauseFlags.remove(at: idx)
            if self.pauseFlags.count == 0 {
                self.play()
            }
        }
    }
    
    public func pause(for flag: Int) {
        //若暂停中且没有暂停标识说明是用户主动暂停的，则所有自动暂停都不生效，防止被自动播放
        //若暂停中且有暂停标识说明是自动暂停的，则新的自动暂停也需要加入，自动播放才可以满足全部时才生效
        if self.status.isPaused && self.pauseFlags.count == 0 {
            return
        }
        if !self.pauseFlags.contains(flag) {
            self.pauseFlags.append(flag)
            if !self.status.isPaused {
                self.currentPlayerAdapter?.pause()
            }
        }
    }
    /// flag为nil则存在任意flag都为true
    public func isPaused(for flag: Int?) -> Bool {
        if let flag = flag {
            return self.pauseFlags.contains(flag)
        }
        return self.pauseFlags.count > 0
    }
    
    /// 根据显示模式重新布局
    func layoutVideoGravity() {
        if let player = self.currentPlayerAdapter {
            let videoSize = player.presentationSize
            let containerSize = self.view.bounds.size
            if videoSize == .zero || containerSize == .zero {
                return
            }
            let videoAspect = videoSize.width / videoSize.height
            let containerAspect = containerSize.width / containerSize.height
            switch self.videoGravity {
            case .aspect:
                player.view.snp.remakeConstraints { make in
                    if videoAspect >= containerAspect {
                        make.left.right.equalToSuperview()
                        make.centerY.equalToSuperview()
                    }else{
                        make.top.bottom.equalToSuperview()
                        make.centerX.equalToSuperview()
                    }
                    make.width.equalTo(player.view.snp.height).multipliedBy(videoAspect)
                }
            case .aspectFill:
                player.view.snp.remakeConstraints { make in
                    if videoAspect < containerAspect {
                        make.left.right.equalToSuperview()
                        make.centerY.equalToSuperview()
                    }else{
                        make.top.bottom.equalToSuperview()
                        make.centerX.equalToSuperview()
                    }
                    make.width.equalTo(player.view.snp.height).multipliedBy(videoAspect)
                }
            case .fill:
                player.view.snp.remakeConstraints { make in
                    make.edges.equalToSuperview()
                }
            case let .resizeAspect(width: x, height: y):
                let aspect = CGFloat(x) / CGFloat(y)
                let fitWidthAspect = aspect >= containerAspect ? 1 : aspect / containerAspect
                let videoWidthAspect = videoAspect >= aspect ? 1 : videoAspect / aspect
                player.view.snp.remakeConstraints { make in
                    make.width.equalToSuperview().multipliedBy(fitWidthAspect * videoWidthAspect)
                    make.center.equalToSuperview()
                    make.width.equalTo(player.view.snp.height).multipliedBy(videoAspect)
                }
            case let .resizeAspectFill(width: x, height: y):
                let aspect = CGFloat(x) / CGFloat(y)
                player.view.snp.remakeConstraints { make in
                    if aspect >= containerAspect {
                        make.left.right.equalToSuperview()
                        make.centerY.equalToSuperview()
                    }else{
                        make.top.bottom.equalToSuperview()
                        make.centerX.equalToSuperview()
                    }
                    make.width.equalTo(player.view.snp.height).multipliedBy(aspect)
                }
            }
        }
    }
}
extension VideoPlayerManager : CorePlayerAdapterDelegate {
    
    public func playerAdapter(_ player: CorePlayerAdapterType, didChangeStatus status: CorePlayerStatus) {
        self.status = status
    }
    
    public func playerAdapter(_ player: CorePlayerAdapterType, didChangePresentationSize size: CGSize) {
        self.layoutVideoGravity()
    }
    
    public func playerAdapter(_ player: CorePlayerAdapterType, didChangeDuration duration: TimeInterval) {
        self.observer.duration.sendActions(for: duration)
    }
    
    public func playerAdapter(_ player: CorePlayerAdapterType, didChangePlayableDuration duration: TimeInterval) {
        self.observer.playableDuration.sendActions(for: duration)
    }
    
    public func playerAdapterReadyForDisplay(_ player: CorePlayerAdapterType) {
        
    }
}
extension VideoPlayerManager {
    class ContainerView: UIView {
        var whenRelayouted: (() -> Void)?
        override func layoutSubviews() {
            super.layoutSubviews()
            self.whenRelayouted?()
        }
    }
}

// MARK: 自动生成暂停标识

fileprivate var __video_player_last_auto_pause_flag = Int.max
///自动生成不重复的暂停标识
public func makePauseFlag() -> Int {
    __video_player_last_auto_pause_flag -= 1
    return __video_player_last_auto_pause_flag
}
